package com.zxmr.navigate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.zxmr.action.Request;
import com.zxmr.action.Response;
import com.zxmr.domain.account.AccountStatus;
import com.zxmr.domain.account.AccountType;
import com.zxmr.domain.account.AuthorizationByRole;
import com.zxmr.domain.account.User;
import com.zxmr.domain.component.Component;
import com.zxmr.domain.service.AuthorizationService;
import com.zxmr.domain.service.UserService;
import com.zxmr.exception.AuthorizeErrorException;
import com.zxmr.exception.InvalidURLException;

public class RequestDispatch {

	final static Map<String,URLElement> urls = new HashMap<String,URLElement>();
	public static Component component = null;
	public static void init(XMLConfigurationLoader loader)
	{
		component = loader.getComponent();
		List<URLElement> elements = loader.getUrls();
		for(URLElement element : elements)
		{
			String url = convertURL(element.getValue());
			urls.put(url, element);
		}
	}
	/**
	 * this function used to convert the URL like /xxx/yyy
	 * @param url
	 * @return
	 */
	public static String convertURL(String url)
	{
		String result = url;
		if(result==null)return null;
		if(!result.startsWith("/"))result = "/"+result;
		if(result.length()>1 && result.endsWith("/"))result = result.substring(0, result.length()-1); 
		return result;
	}
	
	public static Executor getExecutor(Request request)throws InvalidURLException,AuthorizeErrorException
	{ 
		String url = request.getRequestUrl();
		String urlAfterConvert = convertURL(url);
		URLElement element = urls.get(urlAfterConvert);
		if(element == null)
		{
			throw new InvalidURLException(url+" is not an valid URL");
		}
		
		//check if user is authorized to the URL
		authorizeAction(request,element);
		return getExecutorInstance(element);

	}
	/**
	 * check if the request is authorized by comparing URL and request Account
	 * @param request
	 * @param element
	 * @throws AuthorizeErrorException
	 */
	private static void authorizeAction(Request request,URLElement element)throws AuthorizeErrorException
	{
		authorizeUser(request);
		if(isInternalRequest(request,element))
		{
			return;
		}
		authorizeElementAction(request,element);
	}
	public static void forward(Request request,Response response,String url)throws AuthorizeErrorException,InvalidURLException
	{
		String urlAfterConvert = convertURL(url);
		URLElement element = urls.get(urlAfterConvert);
		if(element == null)
		{
			throw new InvalidURLException(url+" is not an valid URL");
		}
		authorizeElementAction(request,element);
		
		Executor executor = getExecutorInstance(element);
		if(executor == null)return;
		
		executor.execute(request, response);
	}
	private static boolean isInternalRequest(Request request,URLElement element)throws AuthorizeErrorException
	{
		if(element.getLevel() == URLAccessLevel.internal && !request.isInternal())
		{
			AuthorizeErrorException excep = new AuthorizeErrorException();
			throw excep;
		}else if(element.getLevel() == URLAccessLevel.internal && request.isInternal()) 
		{
			return true;
		}else
		{
			return false;
		}
	}
	private static void authorizeElementAction(Request request,URLElement element)throws AuthorizeErrorException
	{
		// login user can access to any low level URL
		if(element.getLevel() == URLAccessLevel.low)return;
		// supervisor can access to all exposed actions
		if(request.getAccount().getAccountType() == AccountType.Supervisor)return;
		
		// only supervisor can access to high level URL
		if(element.getLevel() == URLAccessLevel.high)
			throw new AuthorizeErrorException();
		
		AuthorizationService service = new AuthorizationService();
		List<AuthorizationByRole> abr = service.queryAuthorization(request.getAccount().getId(), element.getAction(), component.getId());

		if(abr == null || abr.isEmpty())
		{
			AuthorizeErrorException excep = new AuthorizeErrorException();
			excep.setURL(request.getRequestUrl());
			throw excep;
		}
	}
	private static void authorizeUser(Request request)throws AuthorizeErrorException
	{
		UserService userService = new UserService();
		User user = userService.findUser(request.getAccount().getName());
		if(user == null || user.getId() != request.getAccount().getId() || user.getStatus()!=AccountStatus.Active)
		{
			System.out.println("user information is not valid");
			AuthorizeErrorException excep = new AuthorizeErrorException();
			throw excep;
		}

		//update account information
		request.setAccount(user);
	}
	private static Executor getExecutorInstance(URLElement element)
	{
		String className = element.getClassPath();
		try {
			@SuppressWarnings("unchecked")
			Class<Executor> target = (Class<Executor>)Class.forName(className);
			Executor o = target.newInstance();
			o.setElement(element);
			return o;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
}
